/*
 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;

import java.util.ArrayList;
import java.util.List;

/**
 * 文件描述符类的实例用作底层机器特定结构的不透明句柄，
 * 该结构表示打开的文件、打开的套接字或其他字节源或接收器。
 * 文件描述符的主要实际用途是创建{@link FileInputStream}或{@link FileOutputStream}来包含它。
 *
 * <p> 应用程序不应创建自己的文件描述符。
 *
 * @author  Pavani Diwanji
 * @since   JDK1.0
 */
public final class FileDescriptor {

    private int fd;

    private long handle;

    private Closeable parent;
    private List<Closeable> otherParents;
    private boolean closed;

    /**
     * 构造一个（无效的）FileDescriptor对象。
     */
    public /**/ FileDescriptor() {
        fd = -1;
        handle = -1;
    }

    static {
        initIDs();
    }

    // Set up JavaIOFileDescriptorAccess in SharedSecrets
    static {
        sun.misc.SharedSecrets.setJavaIOFileDescriptorAccess(
            new sun.misc.JavaIOFileDescriptorAccess() {
                public void set(FileDescriptor obj, int fd) {
                    obj.fd = fd;
                }

                public int get(FileDescriptor obj) {
                    return obj.fd;
                }

                public void setHandle(FileDescriptor obj, long handle) {
                    obj.handle = handle;
                }

                public long getHandle(FileDescriptor obj) {
                    return obj.handle;
                }
            }
        );
    }

    /**
     * 标准输入流的句柄。通常，此文件描述符不直接使用，
     * 而是通过称为{@code System.in}的输入流使用。
     *
     * @see     java.lang.System#in
     */
    public static final FileDescriptor in = standardStream(0);

    /**
     * 标准输出流的句柄。通常，此文件描述符不直接使用，
     * 而是通过称为{@code System.out}的输出流使用。
     * @see     java.lang.System#out
     */
    public static final FileDescriptor out = standardStream(1);

    /**
     * 标准错误流的句柄。通常，此文件描述符不直接使用，
     * 而是通过称为{@code System.err}的输出流使用。
     *
     * @see     java.lang.System#err
     */
    public static final FileDescriptor err = standardStream(2);

    /**
     * 测试此文件描述符对象是否有效。
     *
     * @return  如果文件描述符对象表示有效的、打开的文件、套接字或其他活动的I/O连接，
     *          则返回{@code true}；否则返回{@code false}。
     */
    public boolean valid() {
        return ((handle != -1) || (fd != -1));
    }

    /**
     * 强制所有系统缓冲区与底层设备同步。此方法在此FileDescriptor的所有修改数据
     * 和属性都已写入相关设备后返回。特别是，如果此FileDescriptor引用物理存储介质
     * （如文件系统中的文件），sync将不会返回，直到与此FileDescriptor关联的
     * 缓冲区的所有内存中修改副本都已写入物理介质。
     *
     * sync旨在供需要物理存储（如文件）处于已知状态的代码使用。例如，
     * 提供简单事务功能的类可能使用sync来确保给定事务对文件造成的所有更改
     * 都记录在存储介质上。
     *
     * sync仅影响此FileDescriptor下游的缓冲区。如果应用程序正在进行任何内存缓冲
     * （例如，通过BufferedOutputStream对象），则必须将这些缓冲区刷新到
     * FileDescriptor中（例如，通过调用OutputStream.flush），
     * 然后该数据才会受到sync的影响。
     *
     * @exception SyncFailedException
     *        当缓冲区无法刷新时抛出，或者因为系统无法保证所有缓冲区
     *        都已与物理介质同步。
     * @since     JDK1.1
     */
    public native void sync() throws SyncFailedException;

    /* This routine initializes JNI field offsets for the class */
    private static native void initIDs();

    private static native long set(int d);

    private static FileDescriptor standardStream(int fd) {
        FileDescriptor desc = new FileDescriptor();
        desc.handle = set(fd);
        return desc;
    }

    /*
     * 包私有方法来跟踪引用者。
     * 如果多个流指向同一个FileDescriptor，我们循环遍历
     * 所有引用者的列表并调用close()
     */

    /**
     * 将Closeable附加到此FD以进行跟踪。
     * 当需要使closeAll更简单时，parent引用被添加到otherParents中。
     */
    synchronized void attach(Closeable c) {
        if (parent == null) {
            // first caller gets to do this
            parent = c;
        } else if (otherParents == null) {
            otherParents = new ArrayList<>();
            otherParents.add(parent);
            otherParents.add(c);
        } else {
            otherParents.add(c);
        }
    }

    /**
     * 循环遍历共享此FD的所有Closeable并对每个调用close()。
     *
     * 调用者closeable可以调用close0()。
     */
    @SuppressWarnings("try")
    synchronized void closeAll(Closeable releaser) throws IOException {
        if (!closed) {
            closed = true;
            IOException ioe = null;
            try (Closeable c = releaser) {
                if (otherParents != null) {
                    for (Closeable referent : otherParents) {
                        try {
                            referent.close();
                        } catch(IOException x) {
                            if (ioe == null) {
                                ioe = x;
                            } else {
                                ioe.addSuppressed(x);
                            }
                        }
                    }
                }
            } catch(IOException ex) {
                /*
                 * If releaser close() throws IOException
                 * add other exceptions as suppressed.
                 */
                if (ioe != null)
                    ex.addSuppressed(ioe);
                ioe = ex;
            } finally {
                if (ioe != null)
                    throw ioe;
            }
        }
    }
}
